﻿package  
{
	import com.adamatomic.flixel.FlxSprite;
	import com.adamatomic.flixel.FlxText;
    import com.adamatomic.flixel.FlxG;
	
	// Represents a single tile on the board.
	//
	public class Tile
	{
		// Images used for this class.
		//
		[Embed(source = "../data/tilebg.png")] public var mImgBG:Class;

		// The object we use to display the tile.  
		//
		public var mText:FlxText;
        
        // Text to display the possible numbers for each cell.
        //
        public var mPossibleText:FlxText;
		
		// The object used to display the background tile.
		//
		public var mBG:FlxSprite;
		
		// Keeps track of the width of a single tile bg.
		//
		public var mBGWidth:Number = 22;
		
		// When this is true, the tile data has been updated in some way and needs
		// to have it's display updated.
		//
		public var mIsDirty:Boolean;
		
		// Enumerations for the state of a value.
		//
		public static const VALUE_STATE_UNKNOWN:Number 		= 0;
		public static const VALUE_STATE_POSSIBLE:Number 	= 1;
		public static const VALUE_STATE_NOT_POSSIBLE:Number = 2;
		public static const VALUE_STATE_CONFIRMED:Number 	= 3;
		
		// Each tile has a state for each possible number.  We do this
		// so that we can keep track of possible numbers; not just the 
		// confirmed numbers.
		//
		private var mValueStates:Array;
		
		// The number of states.  Basically one for each possible number
		// that can appear on the tile.
		//
		private static const mNumStates:Number				= 9;
		
		// This gets set once we have a confirmed value.
		//
		private var mConfirmedValue:Number;
        
        // Keeps track of if this tile is selected.
        //
        private var mSelected:Boolean;
        
        // Keeps track of tiles which have locked values.  The ones that
        // are given to the user at the start.
        //
        private var mLocked:Boolean;
		
		// Constructor
		//
		public function Tile( ) 
		{
			// Create our array of value states.
			mValueStates = new Array( );
			
            // Start the tile at a default state
			Reset( );
		}
        
        // Resets the tile to its initial state.
        //
        public function Reset( ) : void
        {
			// Loop through all the states setting them to default values
			// as we go.
			for ( var i:Number = 0; i < mNumStates; i++ )
			{
				ClearValueState( i );
			}
			
			mConfirmedValue = -1;
			mIsDirty        = true;
            mSelected       = false;
            mLocked         = false;
        }
		
		// Sets the state of a given number (specified by "index").  newValue should
		// be using one of the VALUE_STATE constants found in this class.
		//
		public function SetValueState( index:Number, newValue:Number ) : void
		{
			// By setting this value state, this instance has become dirty and needs
			// to have it's display string updated.
			mIsDirty = true;
			
			// Store the new state
			mValueStates[index] = newValue;
			
			// If we are confirming a number for this tile than there is some special logic
			// that needs to occur.
			if ( newValue == VALUE_STATE_CONFIRMED )
			{
				// First we need to store the confirmed value of this tile for quick and easy
				// lookups down the road.
				mConfirmedValue = index;
				
				// We also want to loop through every other state and make sure they are mark
				// as not poissible values.
				for ( var i:Number = 0; i < mNumStates; i++ )
				{
					// Make sure we don't set the one we just confirm to "not possible"
					if ( i != index )
					{
						// Recursive
						SetValueState( i, VALUE_STATE_NOT_POSSIBLE );
					}
				}
                
                // Just trying something out.
                //FlxG.quake( 0.010, 0.1 );
			}
		}
		
		// Accessor to the mValueStates array based on index.
		//
		public function GetValueState( index:Number ) : Number
		{
			return mValueStates[index];
		}
        
        // Sets this tile as being highlighted.
        //
        public function SetIsSelected( selected:Boolean ) : void
        {
            if ( selected == mSelected )
            {
                return;
            }
            
            mIsDirty = true;
            mSelected = selected;
        }
		
        // Flags this tile as being locked, meaning it's value can't be changed.  It is
        // one of the tiles given to the user at the start.
        //
        public function SetIsLocked( isLocked:Boolean ) : void
        {
            mLocked = isLocked;
        }
        
		// Sets the vlue of the given state to our "undefined" state.  Should only happen on 
		// initialization, or rseting of the board.
		//
		public function ClearValueState( index:Number ) : void
		{
			SetValueState( index, VALUE_STATE_POSSIBLE );
		}
		
		// Retrieves the string which should be displayed to represent this tile.
		//
		public function GetDisplayString( ) : String
		{
			// If we have confirmed value, display that.  Else, display a character to represent
			// "unknown" values.
			if ( mConfirmedValue != -1 )
			{
				return String( mConfirmedValue + 1 );
			}
			else
			{
				return "";
			}
		}
		
		// Checks if this tile has been solved yet.
		//
		public function HasBeenSolved( ) : Boolean
		{
			return ( Boolean( mConfirmedValue != -1 ) );
		}
		
		// Accessor to the mNumStates member.
		//
		public function GetNumStates( ) : Number
		{
			return mNumStates;
		}
		
		// Accessor for mIsDirty.
		//
		public function IsDirty( ) : Boolean
		{
			return mIsDirty;
		}
		
        // Calling right before the super class begins rendering.
        // Note: Should this class just be made a FlxCore object?
        //
		public function UpdateDisplay( ) : void
		{
			if ( IsDirty( ) )
			{
                if ( mLocked )
                {
                    DisplayAsLocked( );
                }
                else if ( HasBeenSolved( ) )
                {
                    DisplayAsSolved( );
                }
                else
                {
                    DisplayAsUnsolved( );
                }
                
                mIsDirty = false;
			}
		}
        
        private function DisplayAsSolved( ) : void
        {
            if ( mSelected )
            {
                mBG.specificFrame( 3 );
                mText.setColor( 0x660000 );
            }
            else
            {
                mBG.specificFrame( 1 );
                mText.setColor( 0x336666 );
            }
            
            mText.setText( GetDisplayString( ) );
            mText.visible = true;
            mPossibleText.visible = false;
        }
        
        private function DisplayAsUnsolved( ) : void
        {
            if ( mSelected )
            {
                mBG.specificFrame( 2 );
            }
            else
            {
                mBG.specificFrame( 0 );
            }

            mText.visible = false;
            mPossibleText.visible = true;
            
            var possibleAsString:String = "";
            
            // We also want to loop through every other state and make sure they are mark
            // as not poissible values.
            for ( var i:Number = 0; i < mNumStates; i++ )
            {
                if ( i % 3 == 0 && i != 0)
                {
                    possibleAsString += "\n";
                }
                
                // Make sure we don't set the one we just confirm to "not possible"
                if ( GetValueState( i ) == VALUE_STATE_POSSIBLE )
                {
                    possibleAsString += " " + String( i + 1 ) + " ";
                }
                else
                {
                    possibleAsString += "   ";
                }                
            }
            
            mPossibleText.setText( possibleAsString );
        }
        
        private function DisplayAsLocked( ) : void
        {
            if ( mSelected )
            {
                mBG.specificFrame( 2 );
            }
            else
            {
                mBG.specificFrame( 0 );
            }

            mText.setText( GetDisplayString( ) );            
        }
	}
	
}